import 'dart:io';

import 'package:family_expense/shared/services/data_initialization.dart';
import 'package:my_flutter_core/my_flutter_core.dart';

/// The service communicates with backend for [register], [login], [logout] and so on.
class AuthService {
  final String _url = '/authentication';
  final HttpRequestService _httpRequestService = HttpRequestService();

  /// Checks if the username already exists.
  Future<bool> checkOnUsername(String username) async {
    // Gets the response of the http get request.
    final response = await _httpRequestService.get(
      '$_url/check-on-username',
      params: {'username': username},
    );

    // Takes the result from the map.
    final result = response.payload;

    // Parses the data from [Map<String, dynamic>] to [User].
    return result['usernameExists'] as bool;
  }

  /// Registers current username as new account user.
  /// One account group contains users.
  /// The one registers will be the account group admin who has the permission to add other account group users.
  Future<bool> register(
    String username,
    String password,
    String fullName,
  ) async {
    final response = await _httpRequestService.post(
      '$_url/register',
      body: {
        'username': username.trim(),
        'password': password.trim(),
        'fullName': fullName.trim(),
      },
    );

    return response.success;
  }

  /// Unregisters current user. If current user is account group creator this action
  /// will clear all related data. It cannot be undone.
  Future<String> unregister(String password) async {
    final response = await _httpRequestService.post(
      '$_url/unregister',
      body: {'password': password.trim()},
    );

    final result = Map<String, dynamic>.from(response.payload);

    return result['error'].toString();
  }

  /// Login with provided [username] and [password]. The user basic info will be return.
  Future<Map<String, dynamic>> login(String username, String password) async {
    final response = await _httpRequestService.post(
      '$_url/login',
      body: {
        'username': username.trim(),
        'password': password.trim(),
        'deviceInfo': (await getDeviceInfo())!.toJson(),
        'systemInfo': (await getSystemInfo()).toJson(),
      },
    );

    // Returns the data.
    return Map<String, dynamic>.from(response.payload ?? {});
  }

  /// Removes local user info and clears cache data.
  Future<void> logout() async {
    final authStateService = AuthStateService.instance;

    // Clears user jpush registration id to prevent receiving notification after logging out.
    // Even /api/logout is configued as permitAll if request contains deactive token still gets [UnauthorizedError].
    // That's why we neet to igore the error if it happens.
    // try {
    //   await _httpRequestService.post('/api/logout');
    // } finally {
    // Saves current username for next logic form auto fill input.
    authStateService.saveCurrentUsername();

    // Removes local user info.
    authStateService.removeUserInfo();

    // Removes local token info.
    TokenService.removeToken();

    // Clears cache data.
    DataInitialization().clear();

    // Notifies listener to do logout.
    _notifyLogout();
    // }
  }

  Future<Map<String, dynamic>> switchAccountGroup(String accountGroupId) async {
    final response = await _httpRequestService.post(
      '$_url/switch-account-group',
      body: {
        'accountGroupId': accountGroupId.trim(),
        'deviceInfo': (await getDeviceInfo())!.toJson(),
        'systemInfo': (await getSystemInfo()).toJson(),
      },
    );

    // Returns the data.
    return Map<String, dynamic>.from(response.payload);
  }

  Future<void> _notifyLogout() async {
    // Gets logout notifier.
    final logoutNotifier =
        DataSharing.get<SimpleChangeNotifier<LogoutNotifier>>(
          LogoutNotifier.key,
        );

    final logoutNotifierListener = logoutNotifier?.value?.listener;

    // Tries to notify listener to do logout. After that clean this up to avoid duplicate call.
    // We need to do logout for such error but there is not [context] in http request service.
    // That's why we use change notifier.
    logoutNotifier?.change(LogoutNotifier(null));

    // Makes sure listener gets notified.
    sleep(const Duration(milliseconds: 100));

    // Clears notifier.
    logoutNotifier?.removeListener(logoutNotifierListener!);
    logoutNotifier?.dispose();
    DataSharing.remove(key: LogoutNotifier.key);
  }
}
